package com.realphim.app.indie.videoplayer;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;

import org.apache.http.util.ByteArrayBuffer;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.realphim.app.R;
import com.realphim.app.indie.entities.Video;
import com.realphim.app.subtitle.Caption;
import com.realphim.app.subtitle.FormatSRT;
import com.realphim.app.subtitle.TimedTextObject;
import com.realphim.app.subtitle.UnicodeBOMInputStream;

public class VideoPlayerActivity extends Activity implements
        SurfaceHolder.Callback, MediaPlayer.OnPreparedListener,
        VideoControllerView.MediaPlayerControl {

    public final static String PARAM_VIDEO = "video";

    public final static String PARAM_EPISODES = "episodes";

    public final static String PARAM_EPISODE_INDEX = "track_index";

    public final static String PARAM_QUALITY_INDEX = "quality_index";

    SurfaceView videoSurface;

    MediaPlayer player;

    RelativeLayout rlBuffer;

    VideoControllerView controller;

    TextView txtSubtitles;

    boolean isPlayerPrepared;

    boolean shouldPlayerResume;

    int episodeIndex;

    int quality_index = -1;

    Video video;

    ArrayList<String> videoEpsNames;

    ArrayList<Video> episodes;

    int[] videoQualityChoices = new int[] { 0, 0, 0, 0, 0, 0 };

    public final static String[] videoQualityNames = new String[] { "360p",
            "480p", "640p", "mHD", "720p", "1080p" };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_player);

        // TODO: Exceptions on null params
        Intent intent = getIntent();
        video = intent.getParcelableExtra(PARAM_VIDEO);
        episodes = intent.getParcelableArrayListExtra(PARAM_EPISODES);
        episodeIndex = intent.getIntExtra(PARAM_EPISODE_INDEX, 0);
        quality_index = intent.getIntExtra(PARAM_QUALITY_INDEX, -1);

        rlBuffer = (RelativeLayout) findViewById(R.id.rlBuffer);

        SubtitleAsyncTask task = new SubtitleAsyncTask();
        task.execute(video);

        txtSubtitles = (TextView) findViewById(R.id.txtSubtitles);
        videoSurface = (SurfaceView) findViewById(R.id.videoSurface);
        SurfaceHolder videoHolder = videoSurface.getHolder();
        videoHolder.addCallback(this);

        player = new MediaPlayer();
        controller = new VideoControllerView(this);

        Video currentVideo;
        if (episodes == null) {
            currentVideo = video;
            controller.setEpisodeName(currentVideo.getTitle());
        } else {
            currentVideo = episodes.get(episodeIndex);

            // Video episodes
            videoEpsNames = new ArrayList<String>(episodes.size());
            for (int i = 0; i < episodes.size(); i++) {
                videoEpsNames.add(episodes.get(i).getEpisodeTitle());
            }

            ArrayList<String> mEpsName = new ArrayList<String>(videoEpsNames);
            mEpsName.remove(episodeIndex);
            controller.setEpisodeList(mEpsName);
            controller.setEpisodeName(currentVideo.getTitle() + " - "
                    + videoEpsNames.get(episodeIndex));
            // END Video episodes
        }

        // Video quality
        int quality = -1;
        for (int i = 5; i >= 0; i--) {
            if (!TextUtils.isEmpty(currentVideo.getUrls()[i])) {
                videoQualityChoices[i] = 1;
                quality = i;
            }
        }
        if (quality_index >= 0) {
            quality = quality_index;
        }
        quality_index = quality;

        controller.setQualityChoices(quality, videoQualityChoices);
        // END Video quality

        // Video url
        String videoUrl = currentVideo.getUrls()[quality_index];
        // END video url

        try {
            player.setAudioStreamType(AudioManager.STREAM_MUSIC);
            player.setDataSource(this, Uri.parse(videoUrl));
            player.setOnPreparedListener(this);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (isPlayerPrepared && shouldPlayerResume) {
            shouldPlayerResume = false;
            start();
        }
    }

    @Override
    protected void onPause() {
        if (player.isPlaying()) {
            shouldPlayerResume = true;
            pause();
        }
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        player.stop();
        player.release();
        player = null;
        super.onDestroy();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        controller.show();
        return false;
    }

    // Implement SurfaceHolder.Callback
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        player.setDisplay(holder);
        if (!isPlayerPrepared) {
            player.prepareAsync();
            isPlayerPrepared = true;
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    // End SurfaceHolder.Callback

    // Implement MediaPlayer.OnPreparedListener
    @Override
    public void onPrepared(MediaPlayer mp) {
        controller.setMediaPlayer(this);
        controller
                .setAnchorView((FrameLayout) findViewById(R.id.videoSurfaceContainer));

        player.start();

        rlBuffer.setVisibility(View.GONE);

    }

    // End MediaPlayer.OnPreparedListener

    // Implement VideoMediaController.MediaPlayerControl
    @Override
    public boolean canPause() {
        return true;
    }

    @Override
    public boolean canSeekBackward() {
        return true;
    }

    @Override
    public boolean canSeekForward() {
        return true;
    }

    @Override
    public int getBufferPercentage() {
        return 0;
    }

    @Override
    public int getCurrentPosition() {
        return player.getCurrentPosition();
    }

    @Override
    public int getDuration() {
        return player.getDuration();
    }

    @Override
    public boolean isPlaying() {
        return player.isPlaying();
    }

    @Override
    public void pause() {
        player.pause();
    }

    @Override
    public void seekTo(int i) {
        player.seekTo(i);
    }

    @Override
    public void start() {
        player.start();
    }

    @Override
    public boolean isFullScreen() {
        return false;
    }

    @Override
    public void toggleFullScreen() {

    }

    @Override
    public void changeEpisode(int index) {
        int newTrackIndex = index;
        if (index >= episodeIndex) {
            newTrackIndex = index + 1;
        }

        Intent intent = new Intent(VideoPlayerActivity.this,
                VideoPlayerActivity.class);
        intent.putExtra(VideoPlayerActivity.PARAM_VIDEO, this.video);
        intent.putExtra(VideoPlayerActivity.PARAM_EPISODES, this.episodes);
        intent.putExtra(PARAM_EPISODE_INDEX, newTrackIndex);
        intent.putExtra(PARAM_QUALITY_INDEX, 0);
        startActivity(intent);
        finish();
    }

    @Override
    public void changeQuality(int quality) {
        if (quality == quality_index) {
            Toast.makeText(
                    this,
                    "Bạn đang xem video chất lượng "
                            + videoQualityNames[quality], Toast.LENGTH_LONG)
                    .show();
            return;
        }

        Intent intent = new Intent(VideoPlayerActivity.this,
                VideoPlayerActivity.class);
        intent.putExtra(VideoPlayerActivity.PARAM_VIDEO, this.video);
        intent.putExtra(VideoPlayerActivity.PARAM_EPISODES, this.episodes);
        intent.putExtra(PARAM_EPISODE_INDEX, episodeIndex);
        intent.putExtra(PARAM_QUALITY_INDEX, quality);
        startActivity(intent);
        finish();
    }

    // End VideoMediaController.MediaPlayerControl

    // Subtitle
    private TimedTextObject srt;

    private final static int SUBTITLE_DISPLAY_CHECK = 100;

    private Handler subtitleDisplayHandler = new Handler();

    private Runnable subtitle = new Runnable() {
        public void run() {
            if (player != null && player.isPlaying()) {
                int currentPos = player.getCurrentPosition();
                Collection<Caption> subtitles = srt.captions.values();
                for (Caption caption : subtitles) {
                    if (currentPos >= caption.start.getMilliseconds()
                            && currentPos <= caption.end.getMilliseconds()) {
                        onTimedText(caption);
                        break;
                    } else if (currentPos > caption.end.getMilliseconds()) {
                        onTimedText(null);
                    }
                }
            }
            subtitleDisplayHandler.postDelayed(this, SUBTITLE_DISPLAY_CHECK);
        };
    };

    public void onTimedText(Caption text) {
        if (text == null) {
            txtSubtitles.setVisibility(View.INVISIBLE);
            return;
        }
        txtSubtitles.setText(Html.fromHtml(text.content));
        txtSubtitles.setVisibility(View.VISIBLE);
    }

    public class SubtitleAsyncTask extends AsyncTask<Video, Void, Void> {

        private Video video;

        int TIMEOUT_CONNECTION = 7000;

        int TIMEOUT_SOCKET = 7000;

        @SuppressWarnings("resource")
        @Override
        protected Void doInBackground(Video... params) {
            this.video = params[0];

            if (this.video != null) {
                try {
                    runOnUiThread(new Runnable() {
                        public void run() {
                            txtSubtitles.setText("Loading subtitles...");
                        }
                    });

                    File file = downloadSubtitle();
                    InputStream stream = new UnicodeBOMInputStream(
                            new FileInputStream(file)).skipBOM();
                    FormatSRT formatSRT = new FormatSRT();

                    srt = formatSRT.parseFile(file.getName(), stream);
                    runOnUiThread(new Runnable() {
                        public void run() {
                            txtSubtitles
                                    .setText("Loading subtitles finished...");
                        }
                    });
                    subtitleDisplayHandler.post(subtitle);
                } catch (Exception e) {
                    Log.e(getClass().getName(), e.getMessage(), e);
                }
            }

            return null;
        }

        private File downloadSubtitle() throws IOException {

            File root = android.os.Environment.getExternalStorageDirectory();
            File dir = new File(root.getAbsolutePath() + "/realphim");
            if (dir.exists() == false) {
                dir.mkdirs();
            }

            String fileName = video.getTitle() + ".vn.srt";
            URL url = new URL(video.getSubVn());
            File file = new File(dir, fileName);

            if (file.exists()) {
                return file;
            }

            URLConnection uconn = url.openConnection();
            uconn.setReadTimeout(TIMEOUT_CONNECTION);
            uconn.setConnectTimeout(TIMEOUT_SOCKET);

            InputStream is = uconn.getInputStream();
            BufferedInputStream bufferinstream = new BufferedInputStream(is);

            ByteArrayBuffer baf = new ByteArrayBuffer(5000);
            int current = 0;
            while ((current = bufferinstream.read()) != -1) {
                baf.append((byte) current);
            }

            FileOutputStream fos = new FileOutputStream(file);
            fos.write(baf.toByteArray());
            fos.flush();
            fos.close();
            int dotindex = fileName.lastIndexOf('.');
            if (dotindex >= 0) {
                fileName = fileName.substring(0, dotindex);
            }

            return file;
        }

    }
    // End Subtitle

}
